// hdu5731
// 题意：
// 给定一个n*m(<=16*16)的棋盘，现在要用一个1*2或者2*1的矩形无重叠的覆盖满
// 这个棋盘，并且要满足每一行以及每一列都至少有一个矩形横跨。问方案数模
// 1000000007。
//
// 题解：
// 首先我们可以插头dp，或者状压dp不管行列横跨的条件，然后我们枚举列的分割线，
// 进行容斥。容斥的时候我们就不需要对行再做容斥了，可以利用补集转化的思想。
// 假设g[i]为前i行，不管行跨越，在当前某个列容斥的状态s下的方案数，
// 我们要求rows[i]表示前i行，行都有跨越的方案数，那么求rows[i]的时候，
// 初始我们让rows[i]=g[i]，然后对于j，1<=j<i，我们将
//   rows[i] -= rows[j] * g[i-j]
// 不断将前j行合法，第j行没有跨越的方案数减掉就可以算出来。
// 然后根据当前枚举列的分割线个数的奇偶性来决定对答案的贡献是正还是负。
//
// run: time -p $exec < input
// flag: -g
#include <iostream>
#include <cstring>
#include <bitset>
#include <iomanip>
#include <map>

int const maxn = 16;
long long const mo = 1000000007;
long long f[2][1 << (maxn + 2)];
int n, m, len, current;
int crow;

long long lowbit(long long x) { return x & -x; }
int clz(int x) { return __builtin_clz(x); }
int lg2(int x) { return !x ? -1 : 31 - clz(x); }
int count_bits(int x) { return __builtin_popcount(x); }

void init_f(int c)
{
	for (int i = 0; i <= len; i++) f[c][i] = 0;
}

void dfs(int col, int pprow, int prow, int row) // state for previous row and state for current row
{
	if (col == m) {
		f[current][row] += f[current ^ 1][pprow];
		f[current][row] %= mo;
	}

	if (col >= m) return;

	// ep: is empty previous row  ec: is empty current row
	int ep0 = prow & (1<<col), ep1 = prow & (1<<(col+1));
	int ec0 =  row & (1<<col), ec1 =  row & (1<<(col+1));

	// put rectangle vertically
	if (!ep0 && !ec0)
		dfs(col + 1, pprow, prow | (1<<col), row | (1<<col));

	// left empty
	if (ep0)
		dfs(col + 1, pprow, prow, row);

	// put rectangle horizontally
	if (col < m-1 && ep0 && ep1 && !ec0 && !ec1)
		dfs(col + 1, pprow, prow, row | (1<<col) | (1<<(col+1)));
}

long long pre[maxn][maxn];
long long ans[maxn][maxn];
long long g[maxn];
long long rows[maxn];

int main()
{
	for (m = 1; m <= 16; m++) {
		len = 1 << m;
		init_f(current); f[current][len - 1] = 1;
		for (n = 1; n <= m; n++) {
			current ^= 1; init_f(current);
			for (int prow = 0; prow < len; prow++) {
				if (!f[current ^ 1][prow]) continue;
				dfs(0, prow, prow, 0);
			}
			pre[n][m] = f[current][len - 1];
		}
	}

	for (m = 1; m <= 16; m++) {
		for (int k = 0; k < (1 << (m - 1)); k++) {
			for (n = 1; n <= m; n++) {
				g[n] = 1;
				int tk = k | (1 << (m - 1)), p = -1;
				while (tk) {
					int t = lowbit(tk);
					tk ^= t;
					t = lg2(t);
					g[n] *= (n <= t - p) ? pre[n][t - p] : pre[t - p][n];
					g[n] %= mo;
					p = t;
				}
			}

			for (n = 1; n <= m; n++) {
				rows[n] = g[n];
				for (int i = 1; i < n; i++) {
					rows[n] -= rows[i] * g[n - i];
					rows[n] = (rows[n] + mo) % mo;
				}

				if (count_bits(k) & 1) ans[n][m] -= rows[n];
				else ans[n][m] += rows[n];
				ans[n][m] = (ans[n][m] + mo) % mo;
			}
		}
	}

	while (std::cin >> n >> m) {
		if (n > m) std::swap(n, m);
		std::cout << ans[n][m] << "\n";
	}
}

